home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK1.toast / Development Kits (Disc 1) / QuickDraw 3D / Development / 3DMF parser / 0.9 version / MFTEXTRD.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-21  |  30.5 KB  |  1,148 lines  |  [TEXT/MPS ]

  1. /*==============================================================================
  2.  *
  3.  *    File:        MFTEXTRD.C
  4.  *
  5.  *    Function:    Internal Read routines
  6.  *
  7.  *    Author(s):    Rick Wong (RWW), Duet Development Corp.
  8.  *
  9.  *    Copyright:    (c) 1995 by Apple Computer, Inc., all rights reserved.
  10.  *
  11.  *    Change History (most recent first):
  12.  *        Fabio    Changed file name to 8 characters
  13.  *        F3D_RWW    Changed ReadTextString not to call ScanTextBuffer, which
  14.  *                    stripped spaces.
  15.  *        F3A_RWW    TOC stuff works.
  16.  *        F37_RWW    Copy label table so that we can return names with objects.
  17.  *        F2R_RWW    Get rid of dumb curPos field.
  18.  *        F2F_RWW    File created.
  19.  *==============================================================================
  20.  */
  21. #include "MFTEXTRD.H"
  22.  
  23. #include <ctype.h>            /* isspace            */
  24. #include <stddef.h>            /* NULL                */
  25. #include <stdio.h>            /* sscanf            */
  26.  
  27. #include "MF3D.H"
  28. #include "MFERRORS.H"
  29. #include "MFINT64.H"
  30. #include "MFSYSTYP.H"
  31. #include "MFASSERT.H"
  32. #include "MFINTOBJ.H"
  33. #include "MFMEMORY.H"
  34. #include "MFOBJTYP.H"
  35. #include "MFTEXTST.H"
  36. #include "MFTEXTUT.H"
  37.  
  38. /*==============================================================================
  39.  *    MF3D_ReadSingleChar
  40.  *
  41.  *    Read a single character from a text metafile.
  42.  *    If we have time, make this a buffered read for better performance.
  43.  *==============================================================================
  44.  */
  45. char
  46. MF3D_ReadSingleChar(
  47.     MF3D_FilePtr    inMetafilePtr)
  48. {
  49.     MF3DErr    error;
  50.     char    c;
  51.  
  52.     error = (*inMetafilePtr->procsRec.readProc)
  53.                     (inMetafilePtr->userFilePtr, sizeof(char), &c);
  54.     if (error != kMF3DNoErr)
  55.         return kMF3DEOFChar;                /* assume EOF on error */
  56.     else
  57.         return c;
  58. }
  59.  
  60. /*==============================================================================
  61.  *    MF3D_ReadUntilCloseParen
  62.  *
  63.  *    Read characters from a file until we get a close parenthesis
  64.  *    that matches the open parenthesis we just read.
  65.  *==============================================================================
  66.  */
  67. MF3DErr
  68. MF3D_ReadUntilCloseParen(
  69.     MF3D_FilePtr    inMetafilePtr)
  70. {
  71.     MF3DUns32    parenCount;
  72.     char        c;
  73.  
  74.     parenCount = 1;
  75.     do
  76.     {    c = MF3D_ReadSingleChar(inMetafilePtr);
  77.         if (c == kMF3D_CommentLineChar)
  78.         {    /* Entering a comment: read to end of line */
  79.             do
  80.             {    c = MF3D_ReadSingleChar(inMetafilePtr);
  81.             } while (c != '\n' && c != '\r' && c != kMF3DEOFChar);
  82.         }
  83.         else if (c == kMF3D_StringBeginChar)
  84.         {    /* Entering a string: read to end of string */
  85.             do
  86.             {    c = MF3D_ReadSingleChar(inMetafilePtr);
  87.                 if (c == kMF3D_StringEscapeChar)
  88.                 {    /* If we get the escape character,
  89.                      * then ignore the following character.
  90.                      */
  91.                     MF3D_ReadSingleChar(inMetafilePtr);
  92.                     c = MF3D_ReadSingleChar(inMetafilePtr);
  93.                 }
  94.             } while (c != kMF3D_StringEndChar && c != kMF3DEOFChar);
  95.         }
  96.         else if (c == kMF3D_BeginChar)
  97.         {    ++parenCount;
  98.         }
  99.         else if (c == kMF3D_EndChar)
  100.         {    --parenCount;
  101.         }
  102.     } while (parenCount > 0 && c != kMF3DEOFChar);
  103.  
  104.     return (parenCount == 0 ? kMF3DNoErr : kMF3DErrCantParse);
  105. }
  106.  
  107. /*==============================================================================
  108.  *    MF3D_GetTOCLabels
  109.  *
  110.  *    Broke this out of MF3D_PreprocessTextFile
  111.  *==============================================================================
  112.  */
  113. MF3DErr
  114. MF3D_GetTOCLabels(
  115.     MF3D_FilePtr                inMetafilePtr,
  116.     const MF3DUns32                inNumTocs,
  117.     MF3DBinaryFilePositionPtr    inTocLocations,        /* const */
  118.     MF3DUns32                    *outNumLabels,
  119.     MF3D_TOCReferencePtr        *outTocLabelNames,
  120.     MF3DUns32                    *outRefSeed,
  121.     MF3DInt32                    *outTypeSeed)
  122. {
  123.     MF3DUns32                    numTocs;
  124.     MF3DBinaryFilePositionPtr    tocLocations;
  125.     MF3DUns32                    numLabels;
  126.     MF3D_TOCReferencePtr        tocLabelNames;
  127.     MF3DUns32                    refSeed;
  128.     MF3DInt32                    typeSeed;
  129.     MF3DErr                        result;
  130.  
  131.     tocLocations = inTocLocations;
  132.  
  133.     result = kMF3DNoErr;
  134.     numLabels = 0;
  135.     tocLabelNames = MF3D_Malloc(0);
  136.     refSeed = 0;
  137.     typeSeed = 0;
  138.  
  139.     for (numTocs = inNumTocs; numTocs > 0 && result == kMF3DNoErr; --numTocs)
  140.     {    MF3DTableOfContentsObjPtr    tocPtr;
  141.         MF3D_TOCReferencePtr        tempPtr;
  142.         MF3D_TOCEntryPtr            tocEntryPtr;
  143.         MF3DUns32                    numNewLabels;
  144.  
  145.         tocPtr = NULL;
  146.  
  147.         /* Set the file pointer to the next TOC and get the obj there */
  148.         result = MF3DSeekPosition(inMetafilePtr, *tocLocations);
  149.  
  150.         if (result == kMF3DNoErr)
  151.         {    ++tocLocations;
  152.             result = MF3D_IntReadObject(inMetafilePtr,
  153.                     (MF3DVoidObjPtr *)&tocPtr);
  154.         }
  155.  
  156.         if (result == kMF3DNoErr)
  157.         {    if (tocPtr->objectType != kMF3DObjTableOfContents)
  158.                 result = kMF3DErrCantParse;
  159.         }
  160.  
  161.         if (result == kMF3DNoErr)
  162.         {    if (tocPtr->refSeed > refSeed)
  163.                 refSeed = tocPtr->refSeed;
  164.             if (tocPtr->typeSeed < typeSeed)
  165.                 typeSeed = tocPtr->typeSeed;
  166.             numNewLabels = tocPtr->nEntries;
  167.             MFASSERT(numNewLabels >= 0);
  168.  
  169.             if (numNewLabels > 0)
  170.             {    tempPtr = MF3D_Realloc(tocLabelNames,
  171.                         (numLabels + numNewLabels) * sizeof(*tocLabelNames));
  172.                 if (tempPtr == NULL)
  173.                     result = kMF3DErrOutOfMemory;
  174.                 else
  175.                     tocLabelNames = tempPtr;
  176.             }
  177.         }
  178.  
  179.         if (result == kMF3DNoErr)
  180.         {    /* Make copies of all the labels */
  181.             tocEntryPtr = tocPtr->tocEntries;
  182.  
  183.             for (; numNewLabels > 0; --numNewLabels)
  184.             {    MF3DCStringPtr    ptr;
  185.     
  186.                 MFASSERT(tocEntryPtr != NULL);
  187.                 MFASSERT(tocEntryPtr->objLocation != NULL);
  188.                 MFASSERT(tocEntryPtr->objLocation->format == kMF3DFormatText);
  189.                 ptr = MF3D_DuplicateCString(
  190.                         tocEntryPtr->objLocation->location.text);
  191.                 if (ptr == NULL)
  192.                 {    result = kMF3DErrOutOfMemory;
  193.                     break;
  194.                 }
  195.                 tocLabelNames[numLabels].refID = tocEntryPtr->refID;
  196.                 tocLabelNames[numLabels].ref.name = ptr;
  197.                 ++tocEntryPtr;
  198.                 ++numLabels;
  199.             }
  200.         }
  201.  
  202.         /* Done with this TOC object; so dispose it */
  203.         MF3DDisposeObject((MF3DVoidObjPtr)tocPtr);
  204.     }
  205.  
  206.     *outNumLabels = numLabels;
  207.     *outTocLabelNames = tocLabelNames;
  208.     *outRefSeed = refSeed;
  209.     *outTypeSeed = typeSeed;
  210.  
  211.     return result;
  212. }
  213.  
  214. /*==============================================================================
  215.  *    MF3D_ConvertTableLabels
  216.  *
  217.  *    Convert all the labels in ioObjTable to refIDs.
  218.  *    Also frees any matching label names in ioTocLabelNames.
  219.  *==============================================================================
  220.  */
  221. MF3DErr
  222. MF3D_ConvertTableLabels(
  223.     MF3D_FilePtr                inMetafilePtr,
  224.     const MF3DUns32                inNumLabels,
  225.     MF3D_TOCReferencePtr        ioTocLabelNames,
  226.     const MF3DUns32                inNumObjects,
  227.     MF3D_ObjectTableEntryPtr    ioObjTable)
  228. {
  229.     MF3DUns32                    objCount;
  230.     MF3DUns32                    labelIndex;
  231.     MF3D_ObjectTableEntryPtr    objTablePtr;
  232.     MF3DUns32                    labelLen;
  233.     MF3DCStringPtr                objLabelName, tempPtr;
  234.     MF3DErr                        result;
  235.  
  236.     result = kMF3DNoErr;
  237.  
  238.     objLabelName = MF3D_Malloc(0);
  239.     objTablePtr = ioObjTable;
  240.     for (objCount = inNumObjects; objCount > 0; --objCount)
  241.     {    /* Did we mark this object as having a label? */
  242.         if ((labelLen = objTablePtr->objRefID) > 0)
  243.         {    result = MF3DSeekPosition(inMetafilePtr, objTablePtr->objLocation);
  244.             if (result != kMF3DNoErr)
  245.                 break;
  246.  
  247.             tempPtr = MF3D_Realloc(objLabelName, labelLen);
  248.             if (tempPtr == NULL)
  249.             {    result = kMF3DErrOutOfMemory;
  250.                 break;
  251.             }
  252.             objLabelName = tempPtr;
  253.  
  254.             /* Read the object label name from the metafile */
  255.             --labelLen;            /* get rid of the colon */
  256.             result = (*inMetafilePtr->procsRec.readProc)
  257.                     (inMetafilePtr->userFilePtr, labelLen, objLabelName);
  258.             if (result != kMF3DNoErr)
  259.                 break;
  260.  
  261.             /* Replace the colon with EOS */
  262.             objLabelName[labelLen] = '\0';
  263.  
  264.             /* Compare the label name against all the names in the TOC */
  265.             for (labelIndex = 0; labelIndex < inNumLabels; ++labelIndex)
  266.             {    if (MF3D_CompareLabelNames(objLabelName,
  267.                         ioTocLabelNames[labelIndex].ref.name) == 0)
  268.                 {    objTablePtr->objRefID =
  269.                             ioTocLabelNames[labelIndex].refID;
  270.                     /* We no longer free the name because a copy of the ptr
  271.                      * exists in the metafile (tocStuff.references).
  272.                      */
  273.                     /* MF3D_Free(ioTocLabelNames[labelIndex].name); */
  274.                     ioTocLabelNames[labelIndex].ref.name = NULL;
  275.                     break;
  276.                 }
  277.             }
  278.             if (labelIndex >= inNumLabels)
  279.             {    /* Did not find the label. Just ignore it. */
  280.                 objTablePtr->objRefID = kMF3DUnreferencedLabel;
  281.             }
  282.         }
  283.         ++objTablePtr;
  284.     }
  285.  
  286.     MF3D_Free(objLabelName);
  287.  
  288.     return result;
  289. }
  290.  
  291. /*==============================================================================
  292.  *    MF3D_PreprocessTextFile
  293.  *
  294.  *    We determined that we need to read the whole text file to get the TOC.
  295.  *    As long as we are reading the text file, we might as well store object
  296.  *    locations because they will come in handy when we actually read the objects.
  297.  *
  298.  *    Recipe for spaghetti code:
  299.  *        Write source code on a deadline and then realize halfway through that
  300.  *        some basic assumptions are not true. [In other words, my apologies to
  301.  *        the reader; but nobody is supposed to be reading this code anyway.]
  302.  *
  303.  *    Returns:
  304.  *        kMF3DNoErr                    if preprocess succeeded
  305.  *        kMF3DErrOutOfMemory            if unable to create objTable
  306.  *        kMF3DErrCantParse            if file format is incorrect
  307.  *        kMF3DErrObjHasTooManyLabels    if two or more labels for a single object
  308.  *        kMF3DErrIllegalObjName        if object name was way too long or was not
  309.  *                                        followed by an open parenthesis
  310.  *        any error returned by        MF3DTellPosition()
  311.  *==============================================================================
  312.  */
  313. MF3DErr
  314. MF3D_PreprocessTextFile(
  315.     MF3D_FilePtr    inMetafilePtr)
  316. {
  317.     MF3DUns32                    numObjects;
  318.     MF3D_ObjectTableEntryPtr    objTable;
  319.     MF3DBinaryFilePosition        location;
  320.     MF3DUns32                    numTocs;
  321.     MF3DBinaryFilePositionPtr    tocLocations;
  322.     MF3DUns32                    numLabels, labelIndex;
  323.     MF3D_TOCReferencePtr        tocLabelNames;
  324.     MF3DUns32                    refSeed;
  325.     MF3DInt32                    typeSeed;
  326.     MF3DUns32                    containerDepth;
  327.     MF3DErr                        result;
  328.     char                        c;
  329.  
  330.     MFASSERT(inMetafilePtr != NULL);
  331.  
  332.     /* We have not yet found any objects */
  333.     numObjects = 0;
  334.     objTable = MF3D_Malloc(0);
  335.     containerDepth = 0;    /* Contents objects whose end parens we have to match */
  336.  
  337.     /* We have not yet read any of the Table of Contents */
  338.     numTocs = 0;
  339.     tocLocations = MF3D_Malloc(0);
  340.  
  341.     /* We have no label table yet */
  342.     numLabels = 0;
  343.     tocLabelNames = NULL;
  344.  
  345.     /* We start outside any object (root level) */
  346.  
  347.     /* Somewhat lame, but we have to keep calling tell until we hit
  348.      * something (we want to know where we were when we hit something;
  349.      * not where we are after we hit it).
  350.      */
  351.     while ((result = MF3DTellPosition(inMetafilePtr, &location)) == kMF3DNoErr)
  352.     {    /* Read until there is nothing more to read */
  353.         if ((c = MF3D_ReadSingleChar(inMetafilePtr)) == kMF3DEOFChar)
  354.             break;                    /* ### REAL LOOP EXIT ### */
  355.  
  356.         /* Check for end of contents object */
  357.         if (c == kMF3D_EndChar)
  358.         {    if (containerDepth == 0)
  359.             {    result = kMF3DErrCantParse;
  360.                 goto PreprocessTextFileAbort;
  361.             }
  362.             --containerDepth;
  363.         }
  364.         /* Look for a text label or object name */
  365.         else if (!isspace(c))
  366.         {    unsigned int    objNameCharPos;
  367.             char            objName[kMF3D_MaxObjNameLength + 1];
  368.  
  369.             if (numObjects % kMF3D_ObjTableChunk == 0)
  370.             {    MF3D_ObjectTableEntryPtr    tempPtr;
  371.                 tempPtr = MF3D_Realloc(objTable,
  372.                         (numObjects + kMF3D_ObjTableChunk) * sizeof(*objTable));
  373.                 if (tempPtr == NULL)
  374.                 {    result = kMF3DErrOutOfMemory;
  375.                     goto PreprocessTextFileAbort;
  376.                 }
  377.                 objTable = tempPtr;
  378.             }
  379.  
  380.             /* Store location of beginning of object */
  381.             AssignInt64(objTable[numObjects].objLocation, location);
  382.             objTable[numObjects].objRefID = 0;
  383.  
  384.             objNameCharPos = 0;
  385.  
  386.             do
  387.             {    if (objNameCharPos < kMF3D_MaxObjNameLength)
  388.                     objName[objNameCharPos++] = c;
  389.  
  390.                 c = MF3D_ReadSingleChar(inMetafilePtr);
  391.  
  392.                 if (isspace(c) || c == kMF3D_BeginChar)
  393.                 {    /* Object header name has ended */
  394.                     if (objNameCharPos >= kMF3D_MaxObjNameLength)
  395.                     {    result = kMF3DErrIllegalObjName;
  396.                         goto PreprocessTextFileAbort;
  397.                     }
  398.                     objName[objNameCharPos] = '\0';
  399.                     /* Make sure we actually get the open paren */
  400.                     while (c != kMF3D_BeginChar)
  401.                     {    if (!isspace(c) || c == kMF3DEOFChar)
  402.                         {    result = kMF3DErrIllegalObjName;
  403.                             goto PreprocessTextFileAbort;
  404.                         }
  405.                         c = MF3D_ReadSingleChar(inMetafilePtr);
  406.                     }
  407.                     /* Is this a table of contents object? */
  408.                     if (MF3D_CompareObjNames(objName, kMF3DObjTableOfContentsText) == 0)
  409.                     {    /* We found a TOC object. We will want to find
  410.                          * this later; so save the index.
  411.                          */
  412.                         MF3DBinaryFilePositionPtr    tempPtr;
  413.                         tempPtr = MF3D_Realloc(tocLocations,
  414.                                 (numTocs + 1) * sizeof(*tocLocations));
  415.                         if (tempPtr == NULL)
  416.                         {    result = kMF3DErrOutOfMemory;
  417.                             goto PreprocessTextFileAbort;
  418.                         }
  419.                         tocLocations = tempPtr;
  420.                         AssignInt64(tocLocations[numTocs], location);
  421.                         ++numTocs;
  422.  
  423.                         /* Skip to the matching close paren */
  424.                         result = MF3D_ReadUntilCloseParen(inMetafilePtr);
  425.                         break;                /* ### LOOP EXIT ### */
  426.                     }
  427.                     else if (MF3D_CompareObjNames(objName,
  428.                             kMF3DObjContainerText) == 0)
  429.                     {    /* We found a Container object.
  430.                          * Ignore it. AND read no further.
  431.                          */
  432.                         ++containerDepth;
  433.                         break;                /* ### LOOP EXIT ### */
  434.                     }
  435.                     else
  436.                     {    /* We have a non-table of contents object.
  437.                          * Just read until we get the matching end paren.
  438.                          */
  439.                         result = MF3D_ReadUntilCloseParen(inMetafilePtr);
  440.                         break;                /* ### LOOP EXIT ### */
  441.                     }
  442.                     if (result != kMF3DNoErr)
  443.                         goto PreprocessTextFileAbort;
  444.                 }
  445.                 else if (c == kMF3D_LabelChar)
  446.                 {    /* First time through, we store labels as lengths */
  447.                     MF3DBinaryFilePosition    endLabelLoc;
  448.  
  449.                     result = MF3DTellPosition(inMetafilePtr, &endLabelLoc);
  450.  
  451.                     if (objTable[numObjects].objRefID != 0)
  452.                         result = kMF3DErrObjHasTooManyLabels;
  453.  
  454.                     if (result != kMF3DNoErr)
  455.                         goto PreprocessTextFileAbort;
  456.  
  457.                     /* length of label, including colon */
  458.                     objTable[numObjects].objRefID = SubtractUns64(endLabelLoc,
  459.                             location);
  460.  
  461.                     /* reset name pointer and continue looking */
  462.                     objNameCharPos = 0;
  463.                     do
  464.                     {    c = MF3D_ReadSingleChar(inMetafilePtr);
  465.                     } while (isspace(c));
  466.                 }
  467.                 else if (c == kMF3DEOFChar)
  468.                 {    result = kMF3DErrCantParse;
  469.                     goto PreprocessTextFileAbort;
  470.                 }
  471.             } while (1);
  472.  
  473.             ++numObjects;
  474.         }
  475.     }
  476.  
  477.     if (numObjects == 0)
  478.         result = kMF3DErrNoObjectsFound;
  479.  
  480.     if (result == kMF3DNoErr && containerDepth > 0)
  481.         result = kMF3DErrCantParse;
  482.  
  483.     /* Readjust the object table from its chunky size */
  484.     if (result == kMF3DNoErr)
  485.     {    MF3D_ObjectTableEntryPtr    tempPtr;
  486.  
  487.         tempPtr = MF3D_Realloc(objTable, (numObjects + 1) * sizeof(*objTable));
  488.         if (tempPtr == NULL)
  489.         {    result = kMF3DErrOutOfMemory;
  490.         }
  491.         else
  492.         {    objTable = tempPtr;
  493.             /* Store the file length here */
  494.             AssignInt64(objTable[numObjects].objLocation, location);
  495.             objTable[numObjects].objRefID = 0;
  496.  
  497.             inMetafilePtr->objTable.numObjects = numObjects;
  498.             inMetafilePtr->objTable.objects = objTable;
  499.         }
  500.     }
  501.  
  502.     /* Okay, if we get this far, we have read through the entire file
  503.      * successfully!
  504.      *
  505.      * Now, we can use all our fancy read routines because we have
  506.      * all the object sizes.
  507.      *
  508.      * So, read in all the TableOfContents objects and convert all
  509.      * the object labels to refIDs.
  510.      */
  511.     if (result == kMF3DNoErr)
  512.     {    result = MF3D_GetTOCLabels(inMetafilePtr, numTocs, tocLocations,
  513.                 &numLabels, &tocLabelNames, &refSeed, &typeSeed);
  514.     }
  515.  
  516.     /* Save the label table info because we will need it for the
  517.      * names later.
  518.      * NOTE: We can probably rewrite to eliminate MF3D_ConvertTableLabels now,
  519.      * if we want to.
  520.      */
  521.     if (result == kMF3DNoErr)
  522.     {    MF3DSize                tocLabelTableSize;
  523.         MF3D_TOCReferencePtr    tempPtr;
  524.  
  525.         inMetafilePtr->tocStuff.refSeed = refSeed;
  526.         inMetafilePtr->tocStuff.typeSeed = typeSeed;
  527.         inMetafilePtr->tocStuff.numReferences = numLabels;
  528.         tocLabelTableSize = numLabels *
  529.                 sizeof(*inMetafilePtr->tocStuff.references);    
  530.         tempPtr = MF3D_Malloc(tocLabelTableSize);
  531.         if (tempPtr == NULL)
  532.         {    result = kMF3DErrOutOfMemory;
  533.         }
  534.         else
  535.         {    inMetafilePtr->tocStuff.references = tempPtr;
  536.             memcpy(inMetafilePtr->tocStuff.references, tocLabelNames,
  537.                     tocLabelTableSize);
  538.         }
  539.     }
  540.  
  541.     /* Now, go through objTable and change the label lengths to actual
  542.      * ref IDs!
  543.      */
  544.     if (result == kMF3DNoErr)
  545.     {    result = MF3D_ConvertTableLabels(inMetafilePtr, numLabels,
  546.                 tocLabelNames, numObjects, objTable);
  547.     }
  548.  
  549. PreprocessTextFileAbort:
  550.     /* Free any TOC labels that did not find matches */
  551.     for (labelIndex = 0; labelIndex < numLabels; ++labelIndex)
  552.     {    MF3D_Free(tocLabelNames[labelIndex].ref.name);
  553.     }
  554.  
  555.     MF3D_Free(tocLabelNames);
  556.     MF3D_Free(tocLocations);
  557.  
  558.     if (result != kMF3DNoErr)
  559.     {    /* Free the objTable only if we are quitting */
  560.         MF3D_Free(objTable);
  561.     }
  562.     else
  563.     {    MF3DBinaryFilePosition    firstObj;
  564.  
  565.         MF3D_Free(inMetafilePtr->readBuffer.buf);
  566.         inMetafilePtr->readBuffer.buf = NULL;
  567.  
  568.         /* Reset the file pointer to the beginning */
  569.         AssignInt64(firstObj, objTable[0].objLocation);
  570.         result = MF3DSeekPosition(inMetafilePtr, firstObj);
  571.     }
  572.  
  573.     return result;
  574. }
  575.  
  576. /*==============================================================================
  577.  *    MF3D_GetRefNameT
  578.  *
  579.  *    Get reference name
  580.  *
  581.  *    Allocates and returns CStringPtr or NULL if no match found.
  582.  *==============================================================================
  583.  */
  584. MF3DCStringPtr
  585. MF3D_GetRefNameT(
  586.     MF3D_FilePtr        inMetafilePtr,
  587.     MF3DReferenceID        inRefID)
  588. {
  589.     MF3DUns32                refIndex;
  590.     MF3D_TOCReferencePtr    labelTablePtr;
  591.     MF3DCStringPtr            result;
  592.  
  593.     result = NULL;
  594.  
  595.     labelTablePtr = inMetafilePtr->tocStuff.references;
  596.     MFASSERT(inMetafilePtr->tocStuff.numReferences == 0 ||
  597.             labelTablePtr != NULL);
  598.  
  599.     for (refIndex = inMetafilePtr->tocStuff.numReferences;
  600.             refIndex > 0; --refIndex)
  601.     {    if (labelTablePtr->refID == inRefID)
  602.         {    result = MF3D_DuplicateCString(labelTablePtr->ref.name);
  603.             break;
  604.         }
  605.         ++labelTablePtr;
  606.     }
  607.  
  608.     return result;
  609. }
  610.  
  611. /*==============================================================================
  612.  *    MF3D_PostprocessTextFile
  613.  *
  614.  *    Dispose of TOC structures
  615.  *==============================================================================
  616.  */
  617. MF3DErr
  618. MF3D_PostprocessTextFile(
  619.     MF3D_FilePtr        inMetafilePtr)
  620. {
  621.     MF3DUns32                refIndex;
  622.     MF3D_TOCReferencePtr    labelTablePtr;
  623.     MF3DBinaryFilePosition    zero;
  624.  
  625.     /* Do not close things if we were just resolving a reference in this file */
  626.     SetInt64ToZero(zero);
  627.     if (CompareInt64(inMetafilePtr->resStuff.returnLoc, zero) == 0)
  628.     {    labelTablePtr = inMetafilePtr->tocStuff.references;
  629.         MFASSERT(inMetafilePtr->tocStuff.numReferences == 0 ||
  630.                 labelTablePtr != NULL);
  631.     
  632.         for (refIndex = inMetafilePtr->tocStuff.numReferences;
  633.                 refIndex > 0; --refIndex)
  634.         {    MF3D_Free(labelTablePtr->ref.name);
  635.             ++labelTablePtr;
  636.         }
  637.  
  638.         MF3D_Free(inMetafilePtr->tocStuff.references);
  639.         MF3D_Free(inMetafilePtr->objTable.objects);
  640.     }
  641.  
  642.     return kMF3DNoErr;
  643. }
  644.  
  645. /*==============================================================================
  646.  *    MF3D_CompareObjNames
  647.  *
  648.  *    Match the name of an object. Return 0 if it matches; nonzero otherwise.
  649.  *==============================================================================
  650.  */
  651. MF3DInt32
  652. MF3D_CompareObjNames(
  653.     const char        *obj1,
  654.     const char        *obj2)
  655. {
  656.     return MF3D_CompareLabelNames(obj1, obj2);
  657. }
  658.  
  659. /*==============================================================================
  660.  *    MF3D_CompareLabelNames
  661.  *
  662.  *    Match the name of a label. Return 0 if it matches; nonzero otherwise.
  663.  *==============================================================================
  664.  */
  665. MF3DInt32
  666. MF3D_CompareLabelNames(
  667.     const char        *label1,
  668.     const char        *label2)
  669. {
  670.     MFASSERT(label1 != NULL || label2 != NULL);
  671.  
  672.     if (label1 == NULL || label2 == NULL)
  673.         return 1;
  674.     return MF3D_CmpStrInsensitive(label1, label2);
  675. }
  676.  
  677. /*==============================================================================
  678.  *    MF3D_ScanTextBuffer
  679.  *
  680.  *    Basically we just need something that does fscanf for one argument.
  681.  *    After reading the argument, skip spaces and comments that follow.
  682.  *==============================================================================
  683.  */
  684. MF3DErr
  685. MF3D_ScanTextBuffer(
  686.     MF3D_FilePtr    inMetafilePtr,
  687.     const char        *inFormatStr,
  688.     void            *outBuffer)
  689. {
  690.     char        *bufPtr;
  691.     MF3DUns32    bufPos;
  692.     int            numCharsRead;
  693.     char        formatStr[kMF3D_MaxFormatLength + 2];
  694.     MF3DErr        result;
  695.  
  696.     MFASSERT(inMetafilePtr->readBuffer.buf != NULL);
  697.     MFASSERT(strlen(inFormatStr) <= kMF3D_MaxFormatLength);
  698.  
  699.     result = kMF3DNoErr;
  700.  
  701.     bufPos = inMetafilePtr->readBuffer.bufPos;
  702.     bufPtr = inMetafilePtr->readBuffer.buf;
  703.  
  704.     /* Add %n to the format string, so that we can tell how many characters
  705.      * get read.
  706.      */
  707.     strcpy(formatStr, inFormatStr);
  708.     strcat(formatStr, "%n");
  709.     numCharsRead = 0;
  710.     sscanf(&bufPtr[bufPos], formatStr, outBuffer, &numCharsRead);
  711.  
  712.     bufPos += numCharsRead;
  713.     MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  714.  
  715.     if (numCharsRead <= 0)
  716.         result = kMF3DErrCantParse;
  717.  
  718.     MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  719.     inMetafilePtr->readBuffer.bufPos = bufPos;
  720.  
  721.     if (result == kMF3DNoErr)
  722.         result = MF3D_SkipWhitespace(inMetafilePtr);
  723.  
  724.     return result;
  725. }
  726.  
  727. /*==============================================================================
  728.  *    MF3D_SkipWhitespace
  729.  *
  730.  *    Read and ignore whitespace from text buffer
  731.  *==============================================================================
  732.  */
  733. MF3DErr
  734. MF3D_SkipWhitespace(
  735.     MF3D_FilePtr    inMetafilePtr)
  736. {
  737.     char        *bufPtr;
  738.     MF3DUns32    bufPos;
  739.     int            numCharsRead;
  740.     char        commentChar[2];
  741.  
  742.     MFASSERT(inMetafilePtr->readBuffer.buf != NULL);
  743.  
  744.     bufPos = inMetafilePtr->readBuffer.bufPos;
  745.     bufPtr = inMetafilePtr->readBuffer.buf;
  746.  
  747.     /* Skip over any whitespace */
  748.     numCharsRead = 0;    /* init to 0 in case there is no whitespace */
  749.     sscanf(&bufPtr[bufPos], "%*[" kMF3D_WhitespaceList "]%n", &numCharsRead);
  750.     bufPos += numCharsRead;
  751.     MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  752.  
  753.     /* If the next character is the comment character, skip the rest
  754.      * of the line.
  755.      */
  756.     while (sscanf(&bufPtr[bufPos],
  757.                 "%1[" kMF3D_CommentLineStr "]"        /* grab one # char        */
  758.                 "%n",                                /* numChars read        */
  759.                 &commentChar, &numCharsRead) > 0)
  760.     {    bufPos += numCharsRead;
  761.         numCharsRead = 0;
  762.         sscanf(&bufPtr[bufPos],
  763.                 "%*[^" kMF3D_EndOfLineList "]%n",    /* ignore rest to EOL    */
  764.                 &numCharsRead);
  765.         bufPos += numCharsRead;
  766.         MFASSERT(numCharsRead > 0);            /* should have been a newline */
  767.         numCharsRead = 0;
  768.         sscanf(&bufPtr[bufPos],
  769.                 "%*[" kMF3D_WhitespaceList "]%n",    /* skip whitespace        */
  770.                 &numCharsRead);
  771.         bufPos += numCharsRead;
  772.     }
  773.  
  774.     MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  775.     inMetafilePtr->readBuffer.bufPos = bufPos;
  776.  
  777.     return kMF3DNoErr;
  778. }
  779.  
  780. /*==============================================================================
  781.  *    MF3D_ReadOpenParen
  782.  *
  783.  *    Read an open paren character and return
  784.  *==============================================================================
  785.  */
  786. MF3DErr
  787. MF3D_ReadOpenParen(
  788.     MF3D_FilePtr    inMetafilePtr)
  789. {
  790.     char    openParen[2];
  791.  
  792.     return MF3D_ScanTextBuffer(inMetafilePtr, "%1[" kMF3D_BeginCharStr "]",
  793.             openParen);
  794. }
  795.  
  796. /*==============================================================================
  797.  *    MF3D_SkipText
  798.  *
  799.  *    Skip over text (used for skipping over resolution labels).
  800.  *    ###NOTE### will return an error if not least two characters of text
  801.  *==============================================================================
  802.  */
  803. MF3DErr
  804. MF3D_SkipText(
  805.     MF3D_FilePtr    inMetafilePtr)
  806. {
  807.     /* ScanTextBuffer expects at least one void * parameter */
  808.     char    junkChar[2];
  809.  
  810.     return MF3D_ScanTextBuffer(inMetafilePtr,
  811.             "%1c"    /* for junkChar */
  812.             "%*[^" kMF3D_BeginCharStr kMF3D_WhitespaceList "]",
  813.             junkChar);
  814. }
  815.  
  816. /*==============================================================================
  817.  *    MF3D_ReadObjectStuff
  818.  *
  819.  *    Read the object name and get the objStuff.
  820.  *==============================================================================
  821.  */
  822. MF3DErr
  823. MF3D_ReadObjectStuff(
  824.     MF3D_FilePtr        inMetafilePtr,
  825.     MF3D_ObjStuffPtr    *outObjectStuffPtr,
  826.     MF3DObjType            *outObjectType)
  827. {
  828.     char        objName[kMF3D_MaxObjNameLength];
  829.     MF3DErr        result;
  830.  
  831.     result = MF3D_ScanTextBuffer(inMetafilePtr,
  832.             "%[^" kMF3D_BeginCharStr kMF3D_EndCharStr kMF3D_WhitespaceList "]",
  833.             objName);
  834.  
  835.     if (result == kMF3DNoErr)
  836.     {    result = MF3D_FindObjectFromName(objName, outObjectStuffPtr,
  837.                 outObjectType);
  838.     }
  839.  
  840.     return result;
  841. }
  842.  
  843. /*==============================================================================
  844.  *    MF3D_ReadTextString
  845.  *
  846.  *    Read a text string
  847.  *    *outStringPtr only valid if kMF3DNoErr returned.
  848.  *==============================================================================
  849.  */
  850. MF3DErr
  851. MF3D_ReadTextString(
  852.     MF3D_FilePtr        inMetafilePtr,
  853.     MF3DCStringPtr        *outStringPtr)
  854. {
  855.     MF3DErr                result;
  856.     MF3D_BuildString    tempStr;
  857.     char                *bufPtr;
  858.     char                c;
  859.     MF3DUns32            bufPos;
  860.     int                    numCharsRead;
  861.     
  862.  
  863.     result = kMF3DNoErr;
  864.  
  865.     bufPos = inMetafilePtr->readBuffer.bufPos;
  866.     bufPtr = inMetafilePtr->readBuffer.buf;
  867.  
  868.     numCharsRead = 0;
  869.     sscanf(&bufPtr[bufPos], "%1[" kMF3D_StringBeginStr "]%n", &c,
  870.             &numCharsRead);
  871.  
  872.     if (numCharsRead <= 0)
  873.         result = kMF3DErrUnquotedString;
  874.  
  875.     if (result == kMF3DNoErr)
  876.     {    bufPos += numCharsRead;
  877.         MFASSERT(c == kMF3D_StringBeginChar);
  878.         MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  879.  
  880.         result = MF3D_BuildString_New(&tempStr);
  881.     }
  882.  
  883.     /* Scan the text buffer, copying characters until we hit the end quote */
  884.     while (result == kMF3DNoErr)
  885.     {    numCharsRead = 0;
  886.         sscanf(&bufPtr[bufPos], "%c%n", &c, &numCharsRead);
  887.         if (numCharsRead <= 0)
  888.             result = kMF3DErrCantParse;
  889.  
  890.         if (result == kMF3DNoErr)
  891.         {    bufPos += numCharsRead;
  892.             MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  893.  
  894.             if (c == kMF3D_StringEndChar)
  895.             {    MF3D_BuildString_EndString(&tempStr);
  896.                 /* Copy the string pointer. Do NOT delete. */
  897.                 *outStringPtr = tempStr.str;
  898.                 break;        /* ### NORMAL EXIT IS HERE ### */
  899.             }
  900.             else if (c == kMF3D_StringEscapeChar)
  901.             {    sscanf(&bufPtr[bufPos], "%c%n", &c, &numCharsRead);
  902.                 if (numCharsRead <= 0)
  903.                     result = kMF3DErrCantParse;
  904.                 else
  905.                     bufPos += numCharsRead;
  906.  
  907.                 if (result == kMF3DNoErr)
  908.                 {    switch (c)
  909.                     {    case 'a':    c = '\a'; break;
  910.                         case 'b':    c = '\b'; break;
  911.                         case 'f':    c = '\f'; break;
  912.                         case 'n':    c = '\n'; break;
  913.                         case 'r':    c = '\r'; break;
  914.                         case 't':    c = '\t'; break;
  915.                         case 'v':    c = '\v'; break;
  916.                         case '0':    /* NOTE: ### \123 NOT SUPPORTED ### */
  917.                         case '1':
  918.                         case '2':
  919.                         case '3':
  920.                         case '4':
  921.                         case '5':
  922.                         case '6':
  923.                         case '7':
  924.                         case '8':
  925.                         case '9':
  926.                         case 'x':    /* NOTE: ### \x0F NOT SUPPORTED ### */
  927.                         default:    break;
  928.                     }
  929.                     MF3D_BuildString_AddChar(&tempStr, c);
  930.                 }
  931.             }
  932.             else
  933.             {    MF3D_BuildString_AddChar(&tempStr, c);
  934.             }
  935.         }
  936.  
  937.         if (result != kMF3DNoErr)
  938.         {    /* Should already be free */
  939.             MFASSERT(tempStr.str == NULL);
  940.         }
  941.     }
  942.  
  943.     MFASSERT(bufPos <= inMetafilePtr->readBuffer.bufSize);
  944.     inMetafilePtr->readBuffer.bufPos = bufPos;
  945.  
  946.     if (result == kMF3DNoErr)
  947.         result = MF3D_SkipWhitespace(inMetafilePtr);
  948.  
  949.     return result;
  950. }
  951.  
  952. /*==============================================================================
  953.  *    MF3D_ReadTextLabel
  954.  *
  955.  *    Read an unquoted text string
  956.  *    *outStringPtr only valid if kMF3DNoErr returned.
  957.  *==============================================================================
  958.  */
  959. MF3DErr
  960. MF3D_ReadTextLabel(
  961.     MF3D_FilePtr        inMetafilePtr,
  962.     MF3DCStringPtr        *outStringPtr)
  963. {
  964.     MF3DErr                result;
  965.     MF3D_BuildString    tempStr;
  966.  
  967.     result = MF3D_BuildString_New(&tempStr);
  968.  
  969.     /* Scan the text buffer, copying characters until we hit a right arrow */
  970.     while (result == kMF3DNoErr)
  971.     {    char        c;
  972.  
  973.         result = MF3D_ScanTextBuffer(inMetafilePtr,
  974.             "%c", &c);
  975.         if (result == kMF3DNoErr)
  976.         {    if (c == kMF3D_FilePtrChar)
  977.             {    MF3D_BuildString_EndString(&tempStr);
  978.                 /* Copy the string pointer. Do NOT delete. */
  979.                 *outStringPtr = tempStr.str;
  980.                 break;        /* ### NORMAL EXIT IS HERE ### */
  981.             }
  982.             else
  983.             {    MF3D_BuildString_AddChar(&tempStr, c);
  984.             }
  985.         }
  986.  
  987.         if (result != kMF3DNoErr)
  988.         {    /* Should already be free */
  989.             MFASSERT(tempStr.str == NULL);
  990.         }
  991.     }
  992.  
  993.     return result;
  994. }
  995.  
  996. /*==============================================================================
  997.  *    MF3D_BuildString_New
  998.  *    MF3D_BuildString_AddChar
  999.  *    MF3D_BuildString_EndString
  1000.  *    MF3D_BuildString_Delete
  1001.  *
  1002.  *    Temporary routines to dynamically build a string of unknown length.
  1003.  *==============================================================================
  1004.  */
  1005. MF3DErr
  1006. MF3D_BuildString_New(
  1007.     MF3D_BuildString    *outStringPtr)
  1008. {
  1009.     outStringPtr->str = MF3D_Malloc(0);
  1010.     outStringPtr->pos = 0;
  1011.     return kMF3DNoErr;
  1012. }
  1013.  
  1014. MF3DErr
  1015. MF3D_BuildString_AddChar(
  1016.     MF3D_BuildString    *ioStringPtr,
  1017.     char                inAddChar)
  1018. {
  1019.     MF3DErr    result;
  1020.  
  1021.     result = kMF3DNoErr;
  1022.     if ((ioStringPtr->pos % kMF3D_StringBufferChunk) == 0)
  1023.     {    MF3DCStringPtr    tempPtr;
  1024.  
  1025.         tempPtr = MF3D_Realloc(ioStringPtr->str,
  1026.                 ioStringPtr->pos + kMF3D_StringBufferChunk);
  1027.         if (tempPtr == NULL)
  1028.         {    MF3D_BuildString_Delete(ioStringPtr);
  1029.             result = kMF3DErrOutOfMemory;
  1030.         }
  1031.         else
  1032.             ioStringPtr->str = tempPtr;
  1033.     }
  1034.  
  1035.     if (result == kMF3DNoErr)
  1036.         ioStringPtr->str[ioStringPtr->pos++] = inAddChar;
  1037.  
  1038.     return result;
  1039. }
  1040.  
  1041. MF3DErr
  1042. MF3D_BuildString_EndString(
  1043.     MF3D_BuildString    *ioStringPtr)
  1044. {
  1045.     MF3DErr    result;
  1046.  
  1047.     result = MF3D_BuildString_AddChar(ioStringPtr, '\0');
  1048.  
  1049.     if (result == kMF3DNoErr)
  1050.     {    ioStringPtr->str = MF3D_Realloc(ioStringPtr->str, ioStringPtr->pos);
  1051.  
  1052.         /* pointer should still be valid because we never realloc more */
  1053.         MFASSERT(ioStringPtr->str != NULL);
  1054.     }
  1055.  
  1056.     return result;
  1057. }
  1058.  
  1059. MF3DErr
  1060. MF3D_BuildString_Delete(
  1061.     MF3D_BuildString    *ioStringPtr)
  1062. {
  1063.     MF3D_Free(ioStringPtr->str);
  1064.     ioStringPtr->str = NULL;
  1065.     return kMF3DNoErr;
  1066. }
  1067.  
  1068. /*==============================================================================
  1069.  *    MF3D_NumToString
  1070.  *
  1071.  *    Convert an unsigned number to its decimal string equivalent
  1072.  *==============================================================================
  1073.  */
  1074. void
  1075. MF3D_NumToString(
  1076.     const MF3DUns32        inNumber,
  1077.     MF3DCStringPtr        outString)
  1078. {
  1079.     MF3DUns32    n, mod;
  1080.     char        revString[kMF3D_MaxDigitsInUns32 + 1];
  1081.     int            i;
  1082.  
  1083.     n = inNumber;
  1084.     revString[i = kMF3D_MaxDigitsInUns32] = '\0';
  1085.  
  1086.     while (n > 0)
  1087.     {    MFASSERT(i > 0);
  1088.  
  1089.         mod = n - ((n/10) * 10);
  1090.  
  1091.         /* Make no assumptions about character ordering */
  1092.         switch (mod)
  1093.         {    case 0:    revString[--i] = '0'; break;
  1094.             case 1:    revString[--i] = '1'; break;
  1095.             case 2:    revString[--i] = '2'; break;
  1096.             case 3:    revString[--i] = '3'; break;
  1097.             case 4:    revString[--i] = '4'; break;
  1098.             case 5:    revString[--i] = '5'; break;
  1099.             case 6:    revString[--i] = '6'; break;
  1100.             case 7:    revString[--i] = '7'; break;
  1101.             case 8:    revString[--i] = '8'; break;
  1102.             case 9:    revString[--i] = '9'; break;
  1103.             default: MFASSERT(0); break;
  1104.         }
  1105.         n /= 10;
  1106.     }
  1107.  
  1108.     strcpy(outString, &revString[i]);
  1109. }
  1110.  
  1111. /*==============================================================================
  1112.  *    MF3D_TextToHex
  1113.  *
  1114.  *    Convert a text character to its hexadecimal equivalent
  1115.  *==============================================================================
  1116.  */
  1117. MF3DUns8
  1118. MF3D_TextToHex(
  1119.     char inChar)
  1120. {
  1121.     /* Make no assumptions about character ordering */
  1122.     switch(inChar)
  1123.     {    case '0':    return(0);
  1124.         case '1':    return(1);
  1125.         case '2':    return(2);
  1126.         case '3':    return(3);
  1127.         case '4':    return(4);
  1128.         case '5':    return(5);
  1129.         case '6':    return(6);
  1130.         case '7':    return(7);
  1131.         case '8':    return(8);
  1132.         case '9':    return(9);
  1133.         case 'A':
  1134.         case 'a':    return(10);
  1135.         case 'B':
  1136.         case 'b':    return(11);
  1137.         case 'C':
  1138.         case 'c':    return(12);
  1139.         case 'D':
  1140.         case 'd':    return(13);
  1141.         case 'E':
  1142.         case 'e':    return(14);
  1143.         case 'F':
  1144.         case 'f':    return(15);
  1145.         default:    MFASSERT(0); return(0);
  1146.     }
  1147. }
  1148.